#pragma pack(push,1)
#include <elf.h>
#pragma pack(pop)

#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

void * search_symbol_ELF32(void * ELF, const char * symbol)
{
    int i;

    Elf32_Ehdr * header   = ELF;

    Elf32_Shdr * sections = ELF
                          + header->e_shoff
                          ;

    void       * strings = ELF
                         + sections[header->e_shstrndx]
                           .sh_offset
                         ;

    Elf32_Sym * symbols  = NULL;
    void * symbols_names = NULL;

    long symbols_count   = 0;

    for(i = 0; i<header->e_shnum; i++)
        if((sections[i].sh_type == SHT_SYMTAB)
           || (sections[i].sh_type == SHT_DYNSYM))
        {
            if (symbols)
                break;

            symbols = ELF
                    + sections[i]
                      .sh_offset
                    ;

            if (sizeof(Elf32_Sym) != sections[i].sh_entsize)
            {
                fprintf(stderr, "Bad Elf32_Sym size\n");
                return NULL;
            }

            symbols_count = sections[i].sh_size
                          / sections[i].sh_entsize
                          ;

            symbols_names = sections[i].sh_link == 0
                          ? strings
                          : ELF
                            + sections
                              [ sections[i].sh_link ]
                               .sh_offset
                          ;
            break;
        }

    if ( ! (symbols
            && symbols_names
            ))
    {
        fprintf(stderr, "Couldn't find usable symbol stuff\n");
        return NULL;
    }

    for (i = 0; i < symbols_count; i++)
    {
        if (((symbols[i].st_info & STT_FUNC) == 0)
            || (symbols[i].st_name == 0 ))
            continue;

        if (strcmp(symbols_names
                   + symbols[i].st_name
                   , symbol
                   ) == 0)
            return ELF
                   + symbols[i].st_value
                   ;
    }

    return NULL;
}

/* Copy-paste x_x */
void * search_symbol_ELF64(void * ELF, const char * symbol)
{
    int i;

    Elf64_Ehdr * header   = ELF;

    Elf64_Shdr * sections = ELF
                          + header->e_shoff
                          ;

    void       * strings = ELF
                         + sections[header->e_shstrndx]
                           .sh_offset
                         ;

    Elf64_Sym * symbols  = NULL;
    void * symbols_names = NULL;

    long symbols_count   = 0;

    for(i = 0; i<header->e_shnum; i++)
        if((sections[i].sh_type == SHT_SYMTAB)
           || (sections[i].sh_type == SHT_DYNSYM))
        {
            if (symbols)
                break;

            symbols = ELF
                    + sections[i]
                      .sh_offset
                    ;

            if (sizeof(Elf64_Sym) != sections[i].sh_entsize)
            {
                fprintf(stderr, "Bad Elf64_Sym size\n");
                return NULL;
            }

            symbols_count = sections[i].sh_size
                          / sections[i].sh_entsize
                          ;

            symbols_names = sections[i].sh_link == 0
                          ? strings
                          : ELF
                            + sections
                              [ sections[i].sh_link ]
                               .sh_offset
                          ;

            break;
        }

    if ( ! (symbols
            && symbols_names
            ))
    {
        fprintf(stderr, "Couldn't usable symbol stuff");
        return NULL;
    }

    for (i = 0; i < symbols_count; i++)
    {
        if (((symbols[i].st_info & STT_FUNC) == 0)
            || (symbols[i].st_name == 0 ))
            continue;

        if (strcmp(symbols_names
                   + symbols[i].st_name
                   , symbol
                   ) == 0)
            return ELF
                   + symbols[i].st_value
                   ;
    }

    return NULL;
}

void * search_symbol(void * ELF, const char * symbol)
{
    switch(((char *)ELF)[EI_CLASS])
    {
        case ELFCLASS64:
            return search_symbol_ELF64(ELF, symbol);

        case ELFCLASS32:
            return search_symbol_ELF32(ELF, symbol);

        default:
            fprintf(stderr, "Bad elf header\n");
            return NULL;
    }
}
